/**
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNXX0KWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0kdl:;;;:xNMMMMMMMMMMMMMMWWWMMMMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNkc;'..',,,';kWMMMMMMMMMMWKkdodKWMMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNx;',,,',;;''':kXNNWWMWN0xc;,,,';o0NMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNk;.'',;;::;,,,,;::cloxdc,''',,'..',l0WMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWk:,,,,,,,;;;'''',,.'',,,,,,'';,'',,,;xNMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNOo:,,'''''.'',,,'';'',,''.'',;;:;,,,''lKMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWWWWN0xc,'..''.''...''',,,;,,,''',,,,;;;,''.':OWMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMNOllllooc,'''''''.'',;:cccccccc:;,,,,,''',,'',,',xWMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMXd,''''.''.''''',:oxOKXNNWWWNNXKOdc,''''',,'.'',;oXMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMW0c''.''''..''',cx0NWMMMMMMMMMMMMMMWNOl,'',;'''',,,cKMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMNk;'''.''.''.';o0NMMMMMMMMMMMMMMMMMMMMMNOc,;,,,,,''''dNMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMXl''.''''''',o0WMMMMMMMMMMMMMMMMMMMMMMMMWKdc:;,,''''':OWMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMWd'.''.''''cOWMMMMMMMMMMMMMMMMMMMMMMMMMMMMXd:;,,,,,,',cxOKNWMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMO;.''''',dXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWk:,,''''..','';cox0NMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMWO;'.''';xNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMKc',,,'''',,'.'',,cKMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMXl''.'';kNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXl'''',,'',,',,,''lXMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMNd,'''',dNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXl'''''',,;;,,''.'dNMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMWXx;'...'lKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMKl,'''''',;,''.'.,kWMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMWXx:'''.'',kWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWK0OOkkxxxxollcc:lKMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMKd:''.'''..:0MMMMMMMMMMMMMMMMMMMMMMMWXOxdddxxkOO00KXXNNNWWWWWWWWNNNWMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMWx,.''..''''lXMMMMMMMMMMMMMMMMMMMWX0ko:,'.''''''',,,;;::cclooddxxkOO00KXXNNNWWWMMMMMM
 *MMMMMMMMMMMMMMMW0doollc:;;,lXMMMMMMMMMMMMMMMMWXOo;,'''''.'...'''''''''..'..'..'''''',,;;::clloodx0WM
 *MWN0OkkOOO00KKXXNNWWWWNNXK0KWMMMMMMMMMMMMMMW0d:'''''''''''''''''..'''..'''''....''.'''..''''.',lkXWM
 *MMWXOdl;''',,,;;:cclodxkOOKXNNWMMMMMMMMMMNOl;''',:ccoxxkOkkxxxdddoolllcc:::;;;,,,''''''.'''';o0NMMMM
 *MMMMMMNKkl,''''''.'''''''',;:clodxO0NWMNkc,',:ok0XNNXK0OOOO00KKKXXXNNNNNNXXXXKK000OOkkkxxxdx0WMMMMMM
 *MMMMMMMMMW0d:''''''''''.''''''.''',oKNOc,':dOXX0kdol:,,''''',,,;;:::cclloodddxxkkOO000KKXXNNNNWMMMMM
 *MMMMMMMMMMMWXkc''''.''''''''''..':kX0l,;lOXXOdc,''..''.''''''''''''''''''''''''''''',,,;;;:lx0NWMMMM
 *MMMMMMMMMMMMMMXk:''''''''.''''',l0Kx::d0XOd:'''..'''..'''''''''''''''''''''...'''''..'.';lx0NWMMMMMM
 *MMMMMMMMMMMMMMMWXd;'''.''''''',dK0l:d0Kkc,'''.'''''''''.''''''''''''''''''''''''''''',cxKWMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMW0c'.''''''';xXkco00d:''''.''''''''''.'''''''''''''''''.''.''''''',lONMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMXd,'''''';kKxoO0d;''.''''''''''''''''''''''''''''''''''..'.'..'cONMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMNk;'''';kKOkOd:'..'.'''''''''''''''''''''''''''''''''..''.'';dXWMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMWO;'',xXK0x:'.'..''''''''''''''''''''''''''''''''''.''.''':OWMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMWO;,oXNOl,'''..'''''''..'''''''''''''''''''''''',,,,;;;;l0WMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMW0OXNk:,,,;;;;;::::cccllllooooddddxxxxxkkkOOOO000KKKKXXNWMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMNK00KKKXXXXNNNNNWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 *MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
         _                                              _                 _       _   _
     ___| |__   ___ _ __   __ _  ___  _ __   __ _   ___(_)_ __ ___  _   _| | __ _| |_(_) ___  _ __
    / __| '_ \ / _ \ '_ \ / _` |/ _ \| '_ \ / _` | / __| | '_ ` _ \| | | | |/ _` | __| |/ _ \| '_ \
    \__ \ | | |  __/ | | | (_| | (_) | | | | (_| | \__ \ | | | | | | |_| | | (_| | |_| | (_) | | | |
    |___/_| |_|\___|_| |_|\__, |\___/|_| |_|\__, | |___/_|_| |_| |_|\__,_|_|\__,_|\__|_|\___/|_| |_|
                          |___/             |___/
 */

#include "Utility/Configuration/SGConfig.h"
#include "Utility/Logging/SGLogger.h"
#include "Utility/Security/LicenseCheck.h"
#include "Utility/Stacktrace/SGSignal.h"
#ifdef SG_USE_MPI
#include "Utility/Algebra/MpiCommunicator.h"
#include "Utility/Algebra/MpiEnvironment.h"
#endif

#include <iostream>

#include "DataStructure/Input/Solver/Subcase.h"

#include "DBManager/DBServiceFactory.h"
#include "DBManager/Database/DatabaseFactory.h"
#include "DBManager/IService/Input/Subcase/ISubcaseService.h"

#include "Export/ExportAPI/ExportAPI.h"
#include "Import/ImportAPI/ImportAPI.h"

#include "Main/Tools.h"
#include "SGFem/Task/Solver/FEMSolve.h"

#if defined(_WIN64) || defined(WIN32) || defined(_WIN32)
#include <RandomTools/RandomTools.h>
#endif

using SG::DataStructure::Common::Status;
// using SG::DataStructure::FEM::SOLType;
using SG::DataStructure::FEM::Project;

using namespace SG::DBManager;
using SG::Export::SGExporter;
using SG::Import::SGImporter;

int main (int argc, char** argv)
{
// const char* arg[] = { "", "-i", "/home/wxc/test/Buckle_euler_cbar2_2CASE_2.bdf", "-w", "/home/wxc/test/" };
// argc              = 5;
// argv              = const_cast<char**> (arg);

// 检查授权
#ifdef SG_LICENSE_CHECK
    SG::Utility::SGCheckLicense ();
#endif

    // 注册堆栈打印
    SG::Utility::SGSignal::registerSignalHandlers ();

#if defined(_WIN64) || defined(WIN32) || defined(_WIN32)
    std::string path;
    const char* argtemp[3] = { "", "-i", "" };
    if (argc == 1)
    {
        path = OpenFileDialog ();
        if (!path.empty ())
        {
            std::cout << "Selected file: " << path << std::endl;
            argtemp[2] = path.c_str ();
            argc       = 3;
            argv       = const_cast<char**> (argtemp);
        }
    }
#endif

    // Parser command Line
    Project project;
    if (Status::STATUS_Failure == parserCommond (argc, argv, _OUT project))
    {
        help ();
        return 0;
    }

    // 初始化日志：将工作空间传入，支持配置中的 ${WORKSPACE} 占位符
    SG::Utility::SGLogger::initWithWorkspace (project.m_workSpace);

    printSubmitPrompt (project.m_modelFileName);

    // 检测CPU类型并在AMD平台上显示警告
    checkCpuAndWarnIfAmd ();

#if defined(_WIN64) || defined(WIN32) || defined(_WIN32)
#ifdef SG_USE_RANDOM_TOOLS
    // 在win平台，当指定-s参数时，启动随机计算工具
    if (project.showWidget)
    {
        SG::RandomTools::ShowRandomTools (argc, argv);
        return 0;
    }
#endif
#endif

    // 数据库工厂
    DatabaseFactory dbFactory;

    // 初始化Algebra库
    SG::Algebra::Initialize (argc, argv);

// 创建数据库
#ifdef SG_USE_MPI

    // int debugFlag = 0;
    // while (debugFlag == 0)
    // {
    //     sleep (1);
    // }

    SG::Algebra::MpiEnvironment  mpiEnv (argc, argv);
    SG::Algebra::MpiCommunicator mpiComm;

    // 当前分区数据库
    IDatabase* db = dbFactory.GetDatabase (
        DatabaseFactory::HDF5, project.m_workSpace, project.m_jobName + "_" + std::to_string (mpiEnv.world_rank ()) + ".db");
    // 当前分区数据管理器
    auto dbManager = std::make_shared<DBServiceFactory> (db);
    // dbManager->ClearDatabase ();

    // 模拟分区
    // SGImporter (dbManager).Import (project.m_modelFilePath + project.m_modelFileName);
#else
    IDatabase* db = dbFactory.GetDatabase (DatabaseFactory::HDF5, project.m_workSpace, project.m_jobName + ".db");
    // 根据数据库创建Service管理器
    auto dbManager = std::make_shared<DBServiceFactory> (db);
    dbManager->ClearDatabase ();

    // Import FEM Model
    SGImporter importer (dbManager);
    auto       importStatus = importer.Import (project.m_modelFilePath + project.m_modelFileName);
    if (Status::STATUS_Failure == importStatus)
    {
        SGError << "Import Failure !!!";
        return -1;
    }
#endif

    FEMSolve (dbManager, project);

    /** *********************** 结果导出 start ***********************/
    {
        // 导出数据
        SG::DataStructure::Common::Status exportStatus = SG::DataStructure::Common::Status::STATUS_Failure;
#ifdef SG_USE_MPI
        // 断开当前分区与数据库的连接
        db->SaveDatabase ();
        dbManager->DisconnectDatabase ();
        dbFactory.DestroyDatabase (db);

        // 在此等待所有进程都计算完，然后进行数据的导出
        mpiComm.barrier ();

        // 只由0号进程去进行数据汇总
        if (mpiEnv.world_rank () == 0)
        {
            // 主结果数据
            IDatabase*                        mainDB        = nullptr;
            SG::DBManager::DBServiceFactorySP mainDbManager = nullptr;

            mainDB = dbFactory.GetDatabase (DatabaseFactory::HDF5, project.m_workSpace, project.m_jobName + ".db");

            mainDbManager = std::make_shared<DBServiceFactory> (mainDB);
            mainDbManager->ClearDatabase ();

            // 导入模型
            SGImporter (mainDbManager).Import (project.m_modelFilePath + project.m_modelFileName);

            SGExporter exporter (mainDbManager);

            exportStatus = exporter.Export (project, mpiEnv.world_rank ());

            // 断开总数据库的连接
            mainDbManager->DisconnectDatabase ();
            dbFactory.DestroyDatabase (mainDB);

            // 判断是否导出成功
            if (Status::STATUS_Failure == exportStatus)
            {
                SGError << "Export Failure !!!";
                return -1;
            }
        }

#else
        SGExporter exporter (dbManager);
        exportStatus = exporter.Export (project);

        // 判断是否导出成功
        if (Status::STATUS_Failure == exportStatus)
        {
            SGError << "Export Failure !!!";
            return -1;
        }
#endif
    }
    /** *********************** 结果导出 end ***********************/

    // 关闭数据库
#ifndef SG_USE_MPI
    dbManager->DisconnectDatabase ();
    dbFactory.DestroyDatabase (db);
#endif

    // 释放Algebra库
    SG::Algebra::Finalize ();

    printEndPrompt (project.m_jobName);
    return 0;
}
